package org.orbis.communicate.searcher;

import java.util.Arrays;
import java.util.Comparator;
import java.util.Map.Entry;
import org.orbis.communicate.Configuration;
import org.orbis.communicate.FlagsResult;
import org.orbis.communicate.filter.FieldFilter;
import org.orbis.communicate.hash.Hashing;
import org.orbis.communicate.node.Node;
import org.orbis.communicate.rpc.Communicator;
import org.orbis.logging.LogParser;
import org.orbis.memoryblock.dictionary.DictionaryPackage;
import org.orbis.memorymanager.MemoryManager;
import org.orbis.memorymanager.TermInformation;
import org.orbis.search.SearchResultPackage;
import org.orbis.structure.ContentInfo;

public class TermSearcherHost implements Runnable{
	private static MemoryManager memman;
	private SearchResultPackage structure;
	private String term;
	private int termNum;
	private int count;
	private boolean[] contentTypes;
	private static int replication;
	private static Communicator communicator;
	private static Configuration conf;
	private static ContentInfo contentInfo;
	private static FieldFilter filter;
	private int retryCount = 2;
	
	private class ResultComparator implements Comparator<FlagsResult>{
		@Override
		public int compare(FlagsResult arg0, FlagsResult arg1){
			return arg0.ID() < arg1.ID() ? 1 : arg0.ID() > arg1.ID() ? -1 : 0;
		}
	}
	private static ResultComparator comp;

	public TermSearcherHost(SearchResultPackage structure, int count, boolean[] contentTypes){
		this.term = structure.getTerm(count).str();
		this.termNum = count;
		this.structure = structure;
		this.contentTypes = contentTypes;
		this.count = count;
	}

	public TermSearcherHost(SearchResultPackage structure, int count, byte[] contentTypes){
		this.term = structure.getTerm(count).str();
		this.termNum = count;
		this.structure = structure;
		this.contentTypes = new boolean[contentTypes.length];
		for(int i = 0; i < contentTypes.length; i++)
			this.contentTypes[i] = contentTypes[i] == 0? false: true;
		this.count = count;
		if(comp == null) comp = new ResultComparator();
	}

	public static void property(int repl, MemoryManager manager, Communicator comm, Configuration config){
		replication = repl;
		memman = manager;
		communicator = comm;
		conf = config;
		contentInfo = config.contentInfo();
		filter = new FieldFilter();
	}

	public void run(){
		try{
			long hash = Hashing.getTermHash(term);
			Node termNode;
			Node myNode = conf.myNode();
			Entry<Long, Node> currentEntry = conf.ceilingHashEntry(hash);
			termNode = currentEntry.getValue();
			int cluster = conf.nodeList().headSet(termNode).size();

			// search boosting
			termNode = currentEntry.getValue();
			Entry<Long, Node> tempEntry = currentEntry;
			for(int i = 0; i < replication; i++){
				if(tempEntry.getValue() == myNode){
					termNode = tempEntry.getValue();
					break;
				}
				tempEntry = conf.higherHashEntry(tempEntry.getKey());
			}

			LogParser.fine("Search - Node: " + termNode);
			if(termNode == myNode){
				// Search locally
				TermInformation info = memman.searchTerm(term, contentTypes, cluster);
				DictionaryPackage pkg = info.getDictionary();
				if(pkg == null){
					structure.notFound();
					return;
				}
				FlagsResult[][] localInvIndex = new FlagsResult[contentTypes.length][];
				for(int i = 0; i < contentTypes.length; i++){
					localInvIndex[i] =
						memman.getInvertIndex(info.getDictionary().getInvertIndex(i), structure.query());
					
//					if(localInvIndex[i] == null) System.out.println("[DEBUG] Field " + i + ": 0 article");
//					else System.out.println("[DEBUG] Field " + i + ": " + localInvIndex[i].length
//						+ " articles");
				}
				FlagsResult[] result = filter.join(contentTypes, localInvIndex);
				Arrays.sort(result, comp);
				structure.setResult(termNum, result);
			}else{
				FlagsResult[] remoteInvIndex = null;
				for(int i = 0; i < retryCount; i++){
					try{
						long time = LogParser.initTime();
						remoteInvIndex =
							communicator.searchRemote(termNode, structure.query(), cluster, contentInfo);
						LogParser.timerFinest("Search / Post to remote", time);
						structure.setResult(termNum, remoteInvIndex);
						break;
					}catch(Exception e){
						e.printStackTrace();
						LogParser.warning("Retrying search on next node.");
						currentEntry = conf.higherHashEntry(currentEntry.getKey());
						continue;
					}
				}
			}
			// all searches demolish if result's NULL
			if(structure.getResult(count) == null) structure.notFound();
		}catch(Exception e){
			e.printStackTrace();
		}finally{
			structure.countdown();
		}
	}
}
